/*

* Copyright (c) 2007, National University of Singapore (NUS)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without 
* modification, are permitted provided that the following conditions 
* are met:
*
*   * Redistributions of source code must retain the above copyright notice, 
*     this list of conditions,the authors and the following disclaimer.
*   * Redistributions in binary form must reproduce the above copyright notice,
*     this list of conditions,the authors and the following disclaimer in
*     the documentation and/or other materials provided with the distribution.
*   * Neither the name of the university nor the names of its 
*     contributors may be used to endorse or promote products derived from 
*     this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
* POSSIBILITY OF SUCH DAMAGE.
*
* Author: Wenwei Xue (dcsxw@nus.edu.sg)
*
*/
package psg.servicemanager;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.util.Date;
import java.util.Hashtable;
import java.util.Random;
import java.util.Vector;

import app.kernel.api.McsAPI;

/**
 * This class represents the service manager at a physical space gateway (PSG).
 */
public class ServiceManager {
	
	public ContextDataServices cds;	

	
	public String domains [];
	public String attrs [][];
	public String valueTypes[][];
	
	/**
	 * @param domains names of the context domains the physical space belongs to
	 * @param attrs names of the context attributes in each domain
	 * @param EventServerSocketPort port of the server socket to accept event subscription
	 */
	public ServiceManager (String nam, String domains [], String attrs [][], String valueTypes[][], int EventServerSocketPort) {
		
		int i,j;
		for (i=0;i<domains.length;i++)
		{
			if (!ContextDomain.checkDomain(domains[i]))
				{
					System.err.println("[ "+app.io.config.config.resourceName+" ] "+"Invalid name of context domain: " + domains[i]);
					System.exit(1);
				}
				
			for (j=0;j<attrs[i].length;j++)
				if (!ContextAttribute.checkName(attrs[i][j]) || !ContextDomain.checkName(domains[i],attrs[i][j]))
				{
					System.err.println("[ "+app.io.config.config.resourceName+" ] "+"Invalid name of context attribute: " + attrs[i][j] + " in domain " + domains[i]);
					System.exit(1);
				}
		}						
		
		cds = new ContextDataServices(domains, attrs, EventServerSocketPort);
	
		this.domains = domains;
		this.attrs = attrs;
		this.valueTypes = valueTypes;
		/*for enabling GUI*/
	//	cds.psg = new PhysicalSpaceGUI(domains, attrs, cds);
		
		
	}	// ServiceManager
	
	public void Update_Re_register(String CSG_Name, String SCName,String nam){
		
		System.out.println("[ "+app.io.config.config.resourceName+" ] "+"UPDATE RE REGISTER: START");
//		leave(nam);
		ClearConnections(CSG_Name, SCName, nam);
		try{
			
			Thread.sleep(200000);
		}
		catch(Exception e){
			
			
		}
		//Ivan 23 June 2010
		String IPPort = "192.168.110.255:88888";
		
		register(nam, IPPort);
		
	}

		
	public void re_register(String CSG_Name, String SCName, String nam, String refList){
	
		
		
		ClearConnections(CSG_Name, SCName, nam);
		
		
		String result = refList;
		// For Parsing String into multiple neighbours.
		
		//System.out.println("PSG Re-registering: RefList="+refList);
			
		int posi = 0;
		int posi2;
		String getNeighbouringAddress="";				
		Vector<String> PossibleNeighbours = new Vector<String>();

		while ((posi!=-1) && (posi<result.length()-1)){				
			
			posi2 = result.indexOf(";",posi+1);				
			if (getNeighbouringAddress.equals(""))
				getNeighbouringAddress = result.substring(posi,posi2);
			else 
				getNeighbouringAddress = result.substring(posi+1,posi2);				
		//	System.out.println("[ "+kernel.udp.config.config.resourceName+" ] "+"- |"+getNeighbouringAddress+"|");
			
			//myNeighbours.add(getNeighbouringAddress);
			if (getNeighbouringAddress.startsWith("http://"))
				PossibleNeighbours.add(getNeighbouringAddress);			
			posi = posi2;			
		}

		Random randomGenerator = new Random();
		randomGenerator.setSeed(System.currentTimeMillis());
		
		for (int i=0; i<10; i++){

			getNeighbouringAddress = PossibleNeighbours.get(randomGenerator.nextInt(PossibleNeighbours.size()));
			String regResults = ((app.kernel.api.McsAPI)(app.demo.demo.PSGs.get(nam))).registerNeighbour(CSG_Name, SCName,getNeighbouringAddress);
			//System.out.println("[ "+app.io.config.config.resourceName+" ] "+"Register NB Result: "+regResults);
			
			// Set up connection with Neighbouring Peer.
			try{
			
				((app.kernel.api.McsAPI)(app.demo.demo.PSGs.get(nam))).foreignRegisterAsNeighbour(CSG_Name, SCName,nam, getNeighbouringAddress);
				
			}
			catch (Exception e){
				e.printStackTrace();
			}
		}
		
		
		
		
		//register(nam, CSG_Name, SCName, cds.getContext(CSG_Name, SCName));
		

		//String ID_KEY = CSG_Name +"."+ SCName;
		//System.out.println("[ "+kernel.udp.config.config.resourceName+" ] "+"ID_KEY = "+ "|"+ID_KEY+ "|; name = " + nam);
		//System.out.println("[ "+kernel.udp.config.config.resourceName+" ] "+"After Registering, MyNeighBoursListSize = "+((Vector)(((app.kernel.api.McsAPI)app.demo.demo.PSGs.get(nam)).myNB).get(ID_KEY)).size());
		
	}
	
	public void re_register(String CSG_Name, String SCName, String nam){
	
		
		
		ClearConnections(CSG_Name, SCName, nam);
		register(nam, CSG_Name, SCName, cds.getContext(CSG_Name, SCName));
		String ID_KEY = CSG_Name +"."+ SCName;
		//System.out.println("[ "+app.io.config.config.resourceName+" ] "+"ID_KEY = "+ "|"+ID_KEY+ "|; name = " + nam);
		//System.out.println("[ "+app.io.config.config.resourceName+" ] "+"After Registering, MyNeighBoursListSize = "+((Vector)(((app.kernel.api.McsAPI)app.demo.demo.PSGs.get(nam)).myNB).get(ID_KEY)).size());
	}

	public void ClearConnections(String CSG_Name, String SCName, String nam){
		//System.out.println("[ "+app.io.config.config.resourceName+" ] "+"ClearConnections: "+nam+" at "+CSG_Name+"."+SCName);
		((app.kernel.api.McsAPI)app.demo.demo.PSGs.get(nam)).ClearAllNeighbours(CSG_Name, SCName);
		//System.out.println("[ "+app.io.config.config.resourceName+" ] "+"After Clearing Connections, MyNeighBoursListSize = "+((Vector)((app.kernel.api.McsAPI)app.demo.demo.PSGs.get(nam)).myNB.get(CSG_Name+"."+SCName)).size());
		
	}
	
	public void writeFile(String dirName, String content, String CSG_Name, String SCName){
		
		try{
			
	        boolean status = new File("c:/Startup/logs/"+dirName+"/"+CSG_Name+"-"+SCName).mkdirs();  // should succeed			    	        
	        // Create file 
	        System.out.println("[ "+app.io.config.config.resourceName+" ] "+"Creating..  c:\\Startup\\logs\\"+dirName+"\\"+CSG_Name+"-"+SCName+"\\log_"+(System.currentTimeMillis() / 100000)+".file");
	        FileWriter fstream = new FileWriter("c:\\Startup\\logs\\"+dirName+"\\"+CSG_Name+"-"+SCName+"\\log_"+(System.currentTimeMillis()/100000)+".file",true);
	            BufferedWriter out = new BufferedWriter(fstream);
	        
	        out.write(new Date(System.currentTimeMillis()).toLocaleString().replace(" PM", ":"+(System.currentTimeMillis()%1000)+" PM").replace(", 20","")+" -> "+content+"\n\n");
	        //Close the output stream
	        out.close();
		}
		catch(Exception e){
			e.printStackTrace();
			
		}
	}
	
	public void register(String nam, String CSG_Name, String SCName, String previousValue){
		
		long startTime = System.currentTimeMillis();
		Hashtable<String,String> CvTypesList = getContextVariablesTypes();
		try{
					
					String varType="";
					varType = CvTypesList.get(CSG_Name+"."+SCName);
					
					System.out.println("[ "+app.io.config.config.resourceName+" ] "+""+nam);
					if (nam.indexOf(";")<0){
						((app.kernel.api.McsAPI)app.demo.demo.PSGs.get(nam)).register_PSG(nam, CSG_Name, SCName, varType, cds.getContext(CSG_Name, SCName));
						//System.out.println("[ "+app.io.config.config.resourceName+" ] "+"Parent=CSM"+"; "+nam);
					}
					else{
						
						String ParentCSG = nam.substring(0, nam.indexOf(";"));
						String rName = nam.substring(nam.indexOf(";")+1, nam.length());
						
						//System.out.println("[ "+app.io.config.config.resourceName+" ] "+"Parent="+ParentCSG+"; "+rName);
						((app.kernel.api.McsAPI)app.demo.demo.PSGs.get(rName)).register_PSG(ParentCSG, rName, CSG_Name, SCName, varType, cds.getContext(CSG_Name, SCName));
						
					}
					Vector nb =(Vector)((app.kernel.api.McsAPI)app.demo.demo.PSGs.get(nam)).myNB.get(CSG_Name+"."+SCName);
					writeFile("EXPERIMENT - PSG SIM", "PSG Re-Registration Time Take: "+(System.currentTimeMillis()-startTime), CSG_Name, SCName);										
					//System.out.println("[ "+app.io.config.config.resourceName+" ] "+"After Registering, Neighbours Details: "+nb.size());
					
					
				//	for (int j=0; j<nb.size();j++){
						
						//System.out.println("[ "+app.io.config.config.resourceName+" ] "+(j+1)+". "+nb.get(j));
				//	}
							
		}
		catch (Exception E){			
			E.printStackTrace();
		}		
		
	}	
	
	public String register(String nam, String IPPort){
		//format of name:
		//"http://"+defaultIP+ ":"+(defaultPort+(i+calib))+"/xmlrpc"
		Hashtable<String,String> CvTypesList = getContextVariablesTypes();
		String id = "test";
		String CSG = "test";
		System.out.println("in register");
		try{
	
			for (int i=0; i<domains.length; i++){
				
				for (int j=0; j<attrs[i].length; j++){
					String varType="";
					varType = CvTypesList.get(domains[i]+"."+attrs[i][j]);
					
					System.out.println("[1 "+app.io.config.config.resourceName+" ] "+""+nam);
					if (nam.indexOf(";")<0){
						System.out.println("in if");
						/*Check if the value type is numeric. If yes, then pass the mean and variance values here instead of the actual value
						 * To do this, either add a new function taking mean and variance or insert two new fields in the existing code. Have a 
						 * flag saying that instead of the actual value the mean and value pairs should be taken*/
						if(varType.equals("Double"))
							System.out.println("Numeric");
						CSG = domains[i];
						((app.kernel.api.McsAPI)app.demo.demo.PSGs.get(nam)).register_PSG(nam, domains[i], attrs[i][j], varType, cds.getContext(domains[i], attrs[i][j]));
						System.out.println("[ "+app.io.config.config.resourceName+" ] "+"Parent=CSM"+"; "+nam);
					}
					else{
						
						String ParentCSG = nam.substring(0, nam.indexOf(";"));
						String rName = nam.substring(nam.indexOf(";")+1, nam.length());
						
						System.out.println("[ "+app.io.config.config.resourceName+" ] "+"Parent="+domains[i]+"; "+ParentCSG+"; "+rName);
						((app.kernel.api.McsAPI)app.demo.demo.PSGs.get(rName)).register_PSG(domains[i], rName, ParentCSG, attrs[i][j], varType, cds.getContext(domains[i], attrs[i][j]));
						
					}
						
					
					//System.err.println("[ "+kernel.udp.config.config.resourceName+" ] "+domains[i]+"."+attrs[i][j]+"="+cds.getContext(domains[i], attrs[i][j]));
				}
				
			}
			
			id = ((app.kernel.api.McsAPI)app.demo.demo.PSGs.get(nam)).getSessionId(nam, CSG, IPPort);
		}
		catch (Exception E){			
			E.printStackTrace();
		}
		//Modified by Shubhabrata - Add return value here to return session id to the user
		return id;
	}
	
	
	public Vector<String> getListOfContextVariables(){
		
		Vector<String> res = new Vector<String>();
		
		for (int i=0; i< domains.length; i++){
			
			for (int j=0; j<attrs[i].length; j++){
				
				res.add(domains[i]+"."+attrs[i][j]);
				
			}
			
		}
		
		return res;
		
	}
	
	public Hashtable<String, String> getContextVariablesTypes(){
	
		Hashtable<String, String> ContextVarTypes = new Hashtable<String, String> ();
		
		for (int i=0; i< domains.length; i++){
			
			for (int j=0; j<attrs[i].length; j++){
				
				ContextVarTypes.put(domains[i]+"."+attrs[i][j], valueTypes[i][j]);
				
			}
			
		}
		
		
		return ContextVarTypes;
	}
	
	public void leave(String nam, String MSID){
		
		Hashtable<String,String> CvTypesList = getContextVariablesTypes();
		System.out.println("* LEFT *");
		try{
			
			for (int i=0; i<domains.length; i++){
				
				for (int j=0; j<attrs[i].length; j++){
					String varType="";
					varType = CvTypesList.get(domains[i]+"."+attrs[i][j]);
					((app.kernel.api.McsAPI)app.demo.demo.PSGs.get(nam)).leave_PSG(nam, domains[i], attrs[i][j], varType, cds.getContext(domains[i], attrs[i][j]));
					
					System.out.println("[ "+app.io.config.config.resourceName+" ] "+"Leaving PSG: "+varType+"; "+domains[i]+"."+attrs[i][j]);
					
				}
				
			}	
			((app.kernel.api.McsAPI)app.demo.demo.PSGs.get(nam)).deleteSessionId(nam,MSID);
		
		}
		catch (Exception E){			
			E.printStackTrace();
		}		
		
	}
	
	public void leave(McsAPI m, String nam){
		
		Hashtable<String,String> CvTypesList = getContextVariablesTypes();
		System.out.println("* LEFT *");
		try{
			
			for (int i=0; i<domains.length; i++){
				
				for (int j=0; j<attrs[i].length; j++){
					String varType="";
					varType = CvTypesList.get(domains[i]+"."+attrs[i][j]);
					m.leave_PSG(nam, domains[i], attrs[i][j], varType, cds.getContext(domains[i], attrs[i][j]));
					
					System.out.println("[ "+app.io.config.config.resourceName+" ] "+"Leaving PSG: "+varType+"; "+domains[i]+"."+attrs[i][j]);
					
				}
				
			}								
		
		}
		catch (Exception E){			
			E.printStackTrace();
		}		
		
	}

	public void leave(String nam, String CSG_Name, String SCName, String previousValue){
		
		Hashtable<String,String> CvTypesList = getContextVariablesTypes();
		try{
			
					String varType="";
					varType = CvTypesList.get(CSG_Name+"."+SCName);
					((app.kernel.api.McsAPI)app.demo.demo.PSGs.get(nam)).leave_PSG(nam, CSG_Name, SCName, varType, previousValue);
		
		}
		catch (Exception E){			
			E.printStackTrace();
		}		
		
	}
	
	/*
	 * Added by Shubhabrata - This function will be used for registering callbacks by a PSG. A PSG will
	 * give two session identifiers for registering the callback. The first session 
	 */
	
	
	public String registerCallback(String nam, String callerId, String calleeId)
	{
		return ((app.kernel.api.McsAPI)app.demo.demo.PSGs.get(nam)).registerCallback(callerId, calleeId);
	}
	/*
	 * Ivan 23 June 2010
	 */
	public String withdrawCallback(String nam, String callerId, String calleeId){
		return ((app.kernel.api.McsAPI)app.demo.demo.PSGs.get(nam)).withdrawCallback(callerId, calleeId);
	}
	public String getIPPort(String nam, String MSID){
		return ((app.kernel.api.McsAPI)app.demo.demo.PSGs.get(nam)).getIPPort(MSID);
	}
	
	/*
	 * Added by Shubhabrata - This function is used to register the mobility update function and inform
	 * the middleware about a change in IP address
	 */
	
	public void mobilityUpdate(String nam, String sessionId, String IPPort)
	{
		((app.kernel.api.McsAPI)app.demo.demo.PSGs.get(nam)).mobilityUpdate(nam, sessionId, IPPort);
	}
	/*// debugging purpose
	public static void main (String argvs []) {
		
		//String attrs [] = new String [ContextAttribute.attrNames.length + ContextAttribute.eventNames.length];
		//int i,j;
		//for (i=0;i<ContextAttribute.attrNames.length;i++)
			//attrs[i] = ContextAttribute.attrNames[i];
		//for (j=0;j<ContextAttribute.eventNames.length;j++)
			//attrs[i++] = ContextAttribute.eventNames[j];
		//System.out.println("[ "+kernel.udp.config.config.resourceName+" ] "+"Attribute list:");
		//for (i=0;i<attrs.length;i++)
			//System.out.print(attrs[i] + " ");
		//System.out.println("[ "+kernel.udp.config.config.resourceName+" ] "+);
		String domains [] = {"person", "shop", "office"};
		String attrs [][] = {{"name","location", "user_profile"}, 
							 {"name", "hasAdvertisement", "crowd_level"}, 
							 {"room_status","isVacant"},};
		int EventServerSocketPort = 9999;
		
		ServiceManager sm = new ServiceManager(domains, attrs, EventServerSocketPort);
		
		sm.cds.psg.PSFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
								
	}	// main*/
	
}	// class